package com.geekhalo.demo.thread.deadlock.fix;

import com.geekhalo.demo.thread.deadlock.bug.GlobalExecuteService;
import com.geekhalo.lego.core.web.RestResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("thread/deadlock/fix")
@Slf4j
public class DeadlockFixController {
    @Autowired
    private GlobalExecuteService executeService;
    @Autowired
    private SubExecuteService subExecuteService;
    @Autowired
    private GlobalExecuteServiceV2 globalExecuteServiceV2;

    @GetMapping("syncSubmit")
    public RestResult<String> syncSubmit(String taskName){
        this.executeService.submit(new ParentTask());
        return RestResult.success("提交成功");
    }

    @GetMapping("syncSubmit2")
    public RestResult<String> syncSubmit2(String taskName){
        log.info("begin to submit");
        this.globalExecuteServiceV2.submit(new ParentTask2());
        log.info("end to submit");
        return RestResult.success("提交成功");
    }

    class ParentTask implements Callable<Boolean>{

        @Override
        public Boolean call() throws Exception {
            log.info("Begin to Run Parent Task");
            Future<A> aFuture = subExecuteService.submit(new FetchAChildTask());
            doSomeThing(500);
            Future<B> bFuture = subExecuteService.submit(new FetchBChildTask());
            doSomeThing(500);
            C c = buildC(aFuture.get(), bFuture.get());
            Future<Boolean> cFuture = subExecuteService.submit(new SaveCChildTask(c));
            Boolean result = cFuture.get();
            log.info("End to Run Parent Task");
            return result;
        }
    }

    class ParentTask2 implements Callable<Boolean>{

        @Override
        public Boolean call() throws Exception {
            log.info("Begin to Run Parent Task");
            Future<A> aFuture = globalExecuteServiceV2.submit(new FetchAChildTask());
            doSomeThing(500);
            Future<B> bFuture = globalExecuteServiceV2.submit(new FetchBChildTask());
            doSomeThing(500);
            C c = buildC(aFuture.get(), bFuture.get());
            Future<Boolean> cFuture = globalExecuteServiceV2.submit(new SaveCChildTask(c));
            Boolean result = cFuture.get();
            log.info("End to Run Parent Task");
            return result;
        }
    }

    private C buildC(A a, B b) {
        doSomeThing(5);
        log.info("success to build C");
        return new C();
    }

    static class FetchAChildTask implements Callable<A>{
        @Override
        public A call() throws Exception {
            doSomeThing(5);
            log.info("success to fetch A");
            return new A();
        }
    }

    static class FetchBChildTask implements Callable<B>{

        @Override
        public B call() throws Exception {
            doSomeThing(5);
            log.info("success to fetch B");
            return new B();
        }
    }

    static class SaveCChildTask implements Callable<Boolean>{
        private final C c;

        SaveCChildTask(C c) {
            this.c = c;
        }

        @Override
        public Boolean call() throws Exception {
            doSomeThing(10);
            log.info("success to Save C");
            return true;
        }
    }

    private static void doSomeThing(int timeout){
        try {
            TimeUnit.MILLISECONDS.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class A {

    }

    static class B{

    }

    static class C{

    }
}
